package persistencia;

import java.util.List;

import logica.exceptions.AuctionDoesNotExistException;
import logica.exceptions.CategoryAlreadyExistException;
import logica.exceptions.CategoryDoesNotExistException;
import logica.exceptions.UnknownProductException;
import logica.exceptions.UserAlreadyExistException;
import logica.interfaces.AuctionIF;
import logica.interfaces.CategoryIF;
import logica.interfaces.ProductIF;
import logica.interfaces.UserIF;

/**
 * Fachada pela qual eh possivel obter os dados persistentes referentes a users,
 * products, categories e auctions.
 *
 *
 */
public class FachadaPersistence {
	// Definicao da pasta de usuarios
	private final String USERS_DIRECTORY = "./leilaosi/users";
	// Definicao da pasta de produtos
	private final String PRODUCTS_DIRECTORY = "./leilaosi/products";
	// Definicao da pasta de categorias
	private final String CATEGORIES_DIRECTORY = "./leilaosi/categories";
	// Definicao da pasta de leiloes
	private final String AUCTIONS_DIRECTORY = "./leilaosi/auctions";

	// persistencia de usuarios
	private Persistence<UserIF> userPersistence;
	// persistencia de produtos
	private Persistence<ProductIF> productPersistence;
	// persistencia de categorias
	private Persistence<CategoryIF> categoryPersistence;
	// persistencia de leiloes
	private Persistence<AuctionIF> auctionPersistence;

	// lista virtual de usuarios
	private List<UserIF> users;
	// lista virtual de produtos
	private List<ProductIF> products;
	// lista virtual de categorias
	private List<CategoryIF> categories;
	// lista virtual de leiloes
	private List<AuctionIF> auctions;

	// Instancia unica da fachada - SINGLETON
	private static FachadaPersistence instanciaUnica = new FachadaPersistence();

	/**
	 * Criacao de uma fachada de persistencia.
	 */
	protected FachadaPersistence() {
		// criacao das persistencias especificas
		this.userPersistence = new Persistence<UserIF>(this.USERS_DIRECTORY);
		this.productPersistence = new Persistence<ProductIF>(
				this.PRODUCTS_DIRECTORY);
		this.categoryPersistence = new Persistence<CategoryIF>(
				this.CATEGORIES_DIRECTORY);
		this.auctionPersistence = new Persistence<AuctionIF>(
				this.AUCTIONS_DIRECTORY);

		// Obtencao das listas de memoria (usadas para agilizar o acesso a
		// dados)
		this.users = userPersistence.getList();
		this.products = productPersistence.getList();
		this.categories = categoryPersistence.getList();
		this.auctions = auctionPersistence.getList();
	}

	/**
	 * Retorna a instancia unica da fachada. Aqui usamos o singleton.
	 *
	 * @return a instancia unica da fachada.
	 */
	public static FachadaPersistence getInstance() {
		return instanciaUnica;
	}

	/**
	 * Adiciona um usuario a lista virtual de usuarios.
	 *
	 * @param user
	 *            O usuario a ser adicionado.
	 * @throws UserAlreadyExistException
	 *             Se o usuario ja existir na persistencia.
	 */
	public void addUser(UserIF user) throws UserAlreadyExistException {
		if (users.contains(user))
			throw new UserAlreadyExistException();
		this.users.add(user);
	}

	/**
	 * Adiciona um produto a lista virtual de produtos.
	 *
	 * @param product
	 *            O produto a ser adicionado.
	 */
	public void addProduct(ProductIF product) {
		this.products.add(product);
	}

	/**
	 * Adiciona uma categoria a lista virtual de categorias.
	 *
	 * @param category
	 *            A categoria ser adicionada.
	 * @throws CategoryAlreadyExistException
	 *             Se a categoria ja existir na persistencia.
	 */
	public void addCategory(CategoryIF category)
			throws CategoryAlreadyExistException {
		if (categories.contains(category))
			throw new CategoryAlreadyExistException();

		this.categories.add(category);
	}

	/**
	 * Adiciona um leilao a lista virtual de leiloes.
	 *
	 * @param auction
	 *            O leilao a ser adicionado.
	 */
	public void addAuction(AuctionIF auction) {
		this.auctions.add(auction);
	}

	/**
	 * Remove um usuario da lista virtual de usuarios.
	 *
	 * @param user
	 *            O usuario a ser removido.
	 * @return true se a operacao for bem sucedida.
	 */
	public boolean removeUser(UserIF user) {
		return this.users.remove(user);
	}

	/**
	 * Remove um produto da lista virtual de produtos.
	 *
	 * @param product
	 *            O produto a ser removido.
	 * @return true se a operacao for bem sucedida.
	 */
	public boolean removeProduct(ProductIF product) {
		return this.products.remove(product);
	}

	/**
	 * Remove uma categoria da lista virtual de categorias.
	 *
	 * @param category
	 *            A categoria a ser removida.
	 * @return true se a operacao for bem sucedida.
	 */
	public boolean removeCategory(CategoryIF category) {
		return this.categories.remove(category);
	}

	/**
	 * Remove um leilao da lista virtual de leiloes.
	 *
	 * @param auction
	 *            O leilao a ser removido.
	 * @return true se a operacao for bem sucedida.
	 */
	public boolean removeAuction(AuctionIF auction) {
		return this.auctions.remove(auction);
	}

	/**
	 * Retorna um usuario de acordo com o login fornecido.
	 *
	 * @param login
	 *            O login do usuario a ser encontrado.
	 * @return O usuario encontrado ou null se ele nao for encontrado.
	 */
	public UserIF getUser(String login) {
		for (UserIF user : this.users) {
			if (user.getLogin().equals(login))
				return user;
		}
		return null;
	}

	/**
	 * Retorna um produto de acordo com o id fornecido.
	 *
	 * @param id
	 *            O id do produto a ser retornado.
	 * @return O produto com o id especifico ou null se ele nao for encontrado.
	 * @throws UnknownProductException
	 *             Se o produto nao for enocntrado.
	 */
	public ProductIF getProduct(String id) throws UnknownProductException {
		for (ProductIF product : this.products) {
			if (product.getId().equals(id))
				return product;
		}
		throw new UnknownProductException();
	}

	/**
	 * Retorna a categoria de acordo com o id fornecido.
	 *
	 * @param id
	 *            O id da categoria a ser retornada.
	 * @return A categoria com o id especifico ou null se ele nao for
	 *         encontrado.
	 * @throws CategoryDoesNotExistException
	 *             Se a categoria nao for encontrada.
	 */
	public CategoryIF getCategory(String id)
			throws CategoryDoesNotExistException {
		for (CategoryIF category : this.categories) {
			if (category.getId().equals(id))
				return category;
		}
		throw new CategoryDoesNotExistException();

	}

	/**
	 * Retorna um leilao de acordo com o id fornecido.
	 *
	 * @param id
	 *            O id do leilao a ser retornado.
	 * @return O leilao com id especifico ou null se ele nao for encontrado.
	 * @throws AuctionDoesNotExistException
	 *             Se o leilao nao existir na persistencia.
	 */
	public AuctionIF getAuction(String id) throws AuctionDoesNotExistException {
		for (AuctionIF auction : this.auctions) {
			if (auction.getId().equals(id))
				return auction;
		}
		throw new AuctionDoesNotExistException();
	}

	/**
	 * Remove um usuario da lista virtual de usuarios passando o login como
	 * parametro.
	 *
	 * @param login
	 *            O login do usuario a ser removido.
	 * @return True se a operacao for bem sucessida.
	 */
	public boolean removeUser(String login) {
		UserIF user = this.getUser(login);
		if (user != null)
			return this.removeUser(user);

		return false;
	}

	/**
	 * Remove um produto da lista virtual de produtos passando o id como
	 * parametro.
	 *
	 * @param id
	 *            O id do produto a ser removido.
	 * @return True se a operacao for bem sucedida.
	 * @throws UnknownProductException
	 *             Se o produto nao for encontrado.
	 */
	public boolean removeProduct(String id) throws UnknownProductException {
		ProductIF product = this.getProduct(id);
		if (product != null)
			return this.removeProduct(product);

		return false;
	}

	/**
	 * Remove uma categoria da lista virtual de categorias passando o id como
	 * parametro.
	 *
	 * @param id
	 *            O id da categoria a ser removida.
	 * @return True se a operacao for bem sucedida.
	 * @throws CategoryDoesNotExistException
	 *             Se a categoria nao existir.
	 */
	public boolean removeCategory(String id)
			throws CategoryDoesNotExistException {
		CategoryIF category = this.getCategory(id);
		if (category != null)
			return this.removeCategory(category);

		return false;
	}

	/**
	 * Remove um leilao da lista virtual de leiloes passando o id como
	 * parametro.
	 *
	 * @param id
	 *            O id do leilao a ser removido.
	 * @return True se a operacao for bem sucedida.
	 * @throws AuctionDoesNotExistException
	 *             Se o leilao nao existir.
	 */
	public boolean removeAuction(String id) throws AuctionDoesNotExistException {
		AuctionIF auction = this.getAuction(id);
		if (auction != null)
			return this.removeAuction(auction);

		return false;
	}

	/**
	 * Retorna a lista completa de categorias cadastradas.
	 *
	 * @return A lista completa de categorias.
	 */
	public List<CategoryIF> getAllCategories() {
		return this.categories;
	}

	/**
	 * Retorna a lista completa de usuarios cadastrados.
	 *
	 * @return A lista completa de usuarios.
	 */
	public List<UserIF> getAllUsers() {
		return this.users;
	}

	/**
	 * Persiste todas as listas virtuais nos respectivos diretorios.
	 */
	public void persistAll() {
		this.userPersistence.persiste(this.users);
		this.productPersistence.persiste(this.products);
		this.categoryPersistence.persiste(this.categories);
		this.auctionPersistence.persiste(this.auctions);
	}

	/**
	 * Retorna a lista completa de produtos cadastrados.
	 *
	 * @return A lista completa de produtos cadastrados.
	 */
	public List<ProductIF> getAllProducts() {
		return this.products;

	}

	/**
	 * Retorna a lista completa de leiloes cadastrados.
	 *
	 * @return A lista completa de leiloes cadastrados.
	 */
	public List<AuctionIF> getAllAuctions() {
		return this.auctions;
	}
}
